#include <iostream>
#include "UDP.h"
#include <map>
#include <random>
#if _WIN32
#include <ws2tcpip.h>
#else
#include <string.h>
#include <netinet/in.h>
#endif
#define HEAD_SIZE				20
#define MAX_TIME_OUT			10000
struct NetPoint
{
	sockaddr_in addr;
	uint32_t ts;
};
enum PackageState
{
	PSH,
	ACK,
	BKACK
};
#define CVTLL(x) (((long long)(x)) & 0xffffffff)
#define MAKEUSHORT(a, b) ((unsigned short)((CVTLL(a)) << 8 | CVTLL(b)))
#define MAKEUINT(a,b,c,d)((unsigned int)((CVTLL(MAKEUSHORT(a,b)) << 16) | CVTLL(MAKEUSHORT(c,d))))
const uint32_t MSG_LOGIN = MAKEUINT('L', 'O', 'G', 'I');
const uint32_t MSG_LOGOUT = MAKEUINT('L', 'O', 'G', 'O');
const uint32_t MSG_HEARTBEAT = MAKEUINT('H', 'E', 'A', 'R');
const uint32_t MSG_TURN = MAKEUINT('T', 'U', 'R', 'N');
const uint32_t MSG_CLIENTS = MAKEUINT('C', 'L', 'I', 'S');

std::map<uint32_t, NetPoint> points;
int ukbps = 0;
int64_t lastts = 0, mb = 0;
UDP* udp = nullptr;

void errlogcb(const char* log, void* user)
{
	printf("%s\n", log);
}

bool VerifyBufferHead(uint8_t* buffer, uint32_t length)
{
	if (length < HEAD_SIZE)
		return false;
	int sum = 0;
	for (int i = 0; i < 10; i++)
		sum += buffer[i];
	if (sum != *((uint16_t*)(buffer + 10)))
		return false;
	return true;
}

float RandomSingle()
{
	std::random_device rd;
	std::default_random_engine generator(rd());
	std::uniform_real_distribution<float> unif(0.0, 1.0);
	return unif(generator);
}

void recvcb(uint8_t* buffer, uint32_t len, sockaddr_in* remote, void* user)
{
	//if (RandomSingle() < 0.2) return;
	ukbps += len;
	mb += len;
	if (VerifyBufferHead(buffer, len))
	{
		PackageState state = (PackageState) * (uint16_t*)(buffer + 8);
		uint32_t msg, target;
		memcpy(&target, buffer + HEAD_SIZE - sizeof(uint64_t), sizeof(uint32_t));
		memcpy(&msg, buffer + HEAD_SIZE - sizeof(uint64_t) + sizeof(uint32_t), sizeof(uint32_t));
		if (msg == MSG_TURN)
		{
			auto point = points.find(target);
			if (point != points.end())
			{
				//if (RandomSingle() < 0.8)
					udp->SendTo(buffer, len, &point->second.addr);

			}
		}
	}
	else
	{
		if (len == sizeof(uint64_t))
		{
			uint32_t msg, target;
			memcpy(&target, buffer, sizeof(uint32_t));
			memcpy(&msg, buffer + sizeof(uint32_t), sizeof(uint32_t));
			if (msg == MSG_LOGIN)
			{
				NetPoint np;
				memcpy(&np.addr, remote, sizeof(sockaddr_in));
				np.ts = udp->GetTimestamp() / 1000;
				points[target] = np;
				(*(uint64_t*)buffer)++;
				udp->SendTo(buffer, len, remote);
				printf("%u login\n", target);
			}
			else if (msg == MSG_HEARTBEAT)
			{
				memcpy(&points[target].addr, remote, sizeof(sockaddr_in));
				points[target].ts = udp->GetTimestamp() / 1000;
				(*(uint64_t*)buffer)++;
				udp->SendTo(buffer, len, remote);
				//printf("%u heart\n", target);
			}
			else if (msg == MSG_LOGOUT)
			{
				auto p = points.find(target);
				if (p != points.end())
				{
					points.erase(p);
					printf("%u logout\n", target);
				}	
			}
			else if (msg == MSG_CLIENTS)
			{
				(*(uint64_t*)buffer)++;
				uint32_t size = len + points.size() * sizeof(uint32_t);
				uint8_t* data = new uint8_t[size];
				memcpy(data, buffer, len);
				int i = 0;
				for (std::map<uint32_t, NetPoint>::iterator iter = points.begin(); iter != points.end(); iter++)
				{
					uint32_t v = iter->first;
					memcpy(data + len + sizeof(uint32_t) * i, &v, sizeof(uint32_t));
					i++;
				}
				udp->SendTo(data, size, remote);
				delete data;
			}
		}
	}
}


int main(int argc, char* argv[])
{
	udp = new UDP();
	udp->RegisterErrorLogCallback(&errlogcb);
	udp->RegisterRecvCallback(&recvcb);
	int port = 5555;
	if (argc < 2)
		printf("port auto set %d\n", port);
	else
		port = atoi(argv[1]);
	if (!udp->Initialize("0.0.0.0", port))
	{
		printf("bind port:%d error\n", port);
		return -1;
	}
	printf("bind port:%d success\n", port);
	while (true)
	{
		if (UDP::GetTimestamp() - lastts > 1000000)
		{
			lastts = UDP::GetTimestamp();
			uint32_t checkts = lastts / 1000;
			for (std::map<uint32_t, NetPoint>::iterator iter = points.begin(); iter != points.end();)
			{
				
				if (checkts - iter->second.ts > MAX_TIME_OUT)
				{
					printf("%u timeout disconnect\n", iter->first);
					std::map<uint32_t, NetPoint>::iterator re = iter;
					iter++;
					points.erase(re);
					continue;
				}
				iter++;
			}
			printf("rate: %d kbps\ttotal: %d mb\n", ukbps / 1000, mb / 1000000);
			ukbps = 0;
		}
		udp->Update(0);
	}
	delete udp;
	return 0;
}